﻿using JESAI.Core.Websocket.Client.Models;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace JESAI.Core.Websocket.Client
{
    public partial class WebsocketClient
    {
        /// <summary>
        /// 强制重新连接。
        /// 关闭当前websocket流并执行与服务器的新连接。
        /// 如果发生连接错误，它不会抛出异常，但会尝试无限期重新连接。
        /// </summary>
        public Task Reconnect()
        {
            return ReconnectInternal(false);
        }

        /// <summary>
        /// 强制重新连接。
        /// 关闭当前websocket流并执行与服务器的新连接。
        /// 如果发生连接错误，它会抛出异常，并且不会执行任何其他重新连接尝试。
        /// </summary>
        public Task ReconnectOrFail()
        {
            return ReconnectInternal(true);
        }

        /// <summary>
        /// 内部重连方法。
        /// </summary>
        /// <param name="failFast"></param>
        /// <returns></returns>
        private async Task ReconnectInternal(bool failFast)
        {
            //如果正在重新连接，则忽略
            if (!IsStarted)
            {
                _logger.LogDebug(L("客户端未启动，忽略重新连接.."), Name);
                return;
            }

            try
            {
                //用户主动触发重新连接
                await ReconnectSynchronized(ReconnectionType.ByUser, failFast, null).ConfigureAwait(false);
            }
            finally
            {
                _reconnecting = false;
            }
        }

        /// <summary>
        /// 同步重连，加锁
        /// </summary>
        /// <param name="type"></param>
        /// <param name="failFast"></param>
        /// <param name="causedException"></param>
        /// <returns></returns>
        private async Task ReconnectSynchronized(ReconnectionType type, bool failFast, Exception? causedException)
        {
            using (await _locker.LockAsync())
            {
                await Reconnect(type, failFast, causedException);
            }
        }

        /// <summary>
        /// 异步重连
        /// </summary>
        /// <param name="type"></param>
        /// <param name="failFast"></param>
        /// <param name="causedException"></param>
        /// <returns></returns>
        private async Task Reconnect(ReconnectionType type, bool failFast, Exception? causedException)
        {
            IsRunning = false;
            if (_disposing || !IsStarted)
            {
                // 客户端已手动处理或停止
                return;
            }

            _reconnecting = true;

            var disType = TranslateTypeToDisconnection(type);
            var disInfo = DisconnectionInfo.Create(disType, _client, causedException);
            if (type != ReconnectionType.Error)
            {
                _disconnectedSubject.OnNext(disInfo);
                if (disInfo.CancelReconnection)
                {
                    // 用户取消重新连接，不执行任何操作
                    _logger.LogInformation(L("用户取消重新连接，退出。"), Name);
                }
            }

            _cancellation?.Cancel();
            try
            {
                _client?.Abort();
            }
            catch (Exception e)
            {
                _logger.LogError(e, L("中止客户端时出现异常。错误： '{error}'"), Name, e.Message);
            }
            _client?.Dispose();

            if (!IsReconnectionEnabled || disInfo.CancelReconnection)
            {
                // 重新连接已禁用，什么都不做
                IsStarted = false;
                _reconnecting = false;
                return;
            }

            _logger.LogDebug(L("重连中..."), Name);
            _cancellation = new CancellationTokenSource();
            await StartClient(_url, _cancellation.Token, type, failFast).ConfigureAwait(false);
            _reconnecting = false;
        }

        /// <summary>
        /// 激活最后机会检查计时器。
        /// </summary>
        private void ActivateLastChance()
        {
            var timerMs = 1000 * 1;
            _lastChanceTimer = new Timer(LastChance, null, timerMs, timerMs);
        }

        /// <summary>
        /// 停止最后机会检查计时器。
        /// </summary>
        private void DeactivateLastChance()
        {
            _lastChanceTimer?.Dispose();
            _lastChanceTimer = null;
        }

        /// <summary>
        /// 最后机会检查。
        /// </summary>
        /// <param name="state"></param>
        private void LastChance(object? state)
        {
            if (!IsReconnectionEnabled || ReconnectTimeout == null)
            {
                // 重新连接已禁用，什么都不做
                DeactivateLastChance();
                return;
            }

            var timeoutMs = Math.Abs(ReconnectTimeout.Value.TotalMilliseconds);
            var diffMs = Math.Abs(DateTime.UtcNow.Subtract(_lastReceivedMsg).TotalMilliseconds);
            if (diffMs > timeoutMs)
            {
                _logger.LogDebug(L("上一条消息是在｛timeoutMs｝毫秒前收到的。开始自动重新连接。。"), Name, timeoutMs.ToString("F"));

                DeactivateLastChance();
                _ = ReconnectSynchronized(ReconnectionType.NoMessageReceived, false, null);
            }
        }
    }
}
